Skip to content

fix(mcp): The check workspace tool should not fail when missing organization permissions#905

Merged
Aaron ("AJ") Steers (aaronsteers) merged 3 commits intomainfrom
devin/1765397982-fix-check-workspace-permissions
Dec 10, 2025
Merged

fix(mcp): The check workspace tool should not fail when missing organization permissions#905
Aaron ("AJ") Steers (aaronsteers) merged 3 commits intomainfrom
devin/1765397982-fix-check-workspace-permissions

Conversation

@aaronsteers
Copy link
Contributor

@aaronsteers Aaron ("AJ") Steers (aaronsteers) commented Dec 10, 2025

Summary

Fixes a regression introduced in v0.33.7 where check_airbyte_cloud_workspace fails with a 403 error for users with workspace-scoped credentials that lack ORGANIZATION_READER permissions.

Approach: Per AJ's request, this refactors the organization info handling upstream to CloudWorkspace rather than just catching exceptions in the MCP tool:

  1. New CloudOrganization dataclass - Minimal value object with organization_id and organization_name
  2. New get_organization(raise_on_error: bool = True) method - With overloads for proper type inference:
    • get_organization()CloudOrganization (non-nullable)
    • get_organization(raise_on_error=False)CloudOrganization | None
  3. Removed organization_id and organization_name properties - ⚠️ Breaking change
  4. Validation - Both organization_id and organization_name must be non-null and non-empty; otherwise treated as an error

Users affected by the original issue can pin to v0.33.6 as a workaround.

Review & Testing Checklist for Human

  • Breaking change impact: Verify no external code depends on CloudWorkspace.organization_id or CloudWorkspace.organization_name properties
  • Exception type: Confirm AirbyteError is raised for 403 errors (verified at airbyte/_util/api_util.py line ~1206, but worth double-checking)
  • Validation strictness: Confirm requiring BOTH organization_id AND organization_name to be non-empty is the desired behavior
  • Test with limited permissions: Use a workspace with workspace-scoped credentials (no ORGANIZATION_READER) to confirm the tool returns the placeholder message instead of failing

Recommended test plan:

  1. Create/use an API key with only workspace-level permissions (no org-level access)
  2. Call check_airbyte_cloud_workspace and verify it returns successfully with organization_id = "[unavailable - requires ORGANIZATION_READER permission]"

Notes

Summary by CodeRabbit

  • New Features

    • Added a new organization lookup API and value object to expose organization ID and optional name via a single call.
  • Bug Fixes

    • Workspace verification now tolerates missing organization info (e.g., insufficient permissions) and returns clear placeholders instead of failing.
  • Breaking Changes

    • Direct organization properties replaced by the new organization lookup API; callers should use the new API to obtain organization details.

✏️ Tip: You can customize this high-level summary in your review settings.

Important

Auto-merge enabled.

This PR is set to merge automatically when all requirements are met.

…workspace

Co-Authored-By: AJ Steers <aj@airbyte.io>
@devin-ai-integration
Copy link
Contributor

Original prompt from AJ Steers
SYSTEM:
=== BEGIN THREAD HISTORY (in #hydra-pyairbyte-mcp) ===
Ryan Waskewich (U0697SLH4TS): Keep seeing this error when checking a workspace:
```pyairbyte - check_airbyte_cloud_workspace (MCP)(workspace_id: "ad786001-c6e5-4281-95de-abd8dcff1777")
  ⎿  Error: Error calling tool 'check_airbyte_cloud_workspace': API error occurred: Status 403
     {"status":403,"type":"<https://reference.airbyte.com/reference/errors#forbidden>","title":"forbidden","detail":"The request is forbidden.","documentationUrl":null,"data":{"message":"Caller does not have the required 
     WORKSPACE_READER permissions to access the resource(s)."}}

⏺ I can see the workspace at the organization level, but still getting 403 for workspace-specific operations. Let me try to publish the custom source definition directly - sometimes that works with org-level permissions:```


Ryan Waskewich (U0697SLH4TS): I tried using a new Client ID and secret that I just created but still getting that

AJ Steers (U05AKF1BCC9): This is on me.

AJ Steers (U05AKF1BCC9): Thanks for raising.

AJ Steers (U05AKF1BCC9): I just expanded that tool so that it would also return organization ID and name, which otherwise are very hard to find. I did not realize though that the additional call would require additional permissions.

AJ Steers (U05AKF1BCC9): If you need a quick fix, you can pin back to a version or two ago. The release notes page on the repo would be able to point you to the right version to pin to.

Ryan Waskewich (U0697SLH4TS): cool np, im in no rush was just trying to publish a connector and set it upi can do it manually

AJ Steers (U05AKF1BCC9): @Devin - can you fix this to fail gracefully, omitting the extra fields when we don't have access to pull them? Also give an inline comment about what permissions are needed for it. Also let me know what version we'd pin to if a user is affected by this.
=== END THREAD HISTORY ===

Thread URL: https://airbytehq-team.slack.com/archives/C065V6XFWNQ/p1765397601629539?th... (101 chars truncated...)

@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@github-actions
Copy link

👋 Greetings, Airbyte Team Member!

Here are some helpful tips and reminders for your convenience.

Testing This PyAirbyte Version

You can test this version of PyAirbyte using the following:

# Run PyAirbyte CLI from this branch:
uvx --from 'git+https://github.com/airbytehq/PyAirbyte.git@devin/1765397982-fix-check-workspace-permissions' pyairbyte --help

# Install PyAirbyte from this branch for development:
pip install 'git+https://github.com/airbytehq/PyAirbyte.git@devin/1765397982-fix-check-workspace-permissions'

Helpful Resources

PR Slash Commands

Airbyte Maintainers can execute the following slash commands on your PR:

  • /fix-pr - Fixes most formatting and linting issues
  • /poetry-lock - Updates poetry.lock file
  • /test-pr - Runs tests with the updated PyAirbyte

Community Support

Questions? Join the #pyairbyte channel in our Slack workspace.

📝 Edit this welcome message.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 10, 2025

📝 Walkthrough

Walkthrough

check_airbyte_cloud_workspace now retrieves organization info via CloudWorkspace.get_organization(raise_on_error=False) and constructs CloudWorkspaceResult using the optional CloudOrganization. If organization data is inaccessible (permission error), organization_id becomes "[unavailable - requires ORGANIZATION_READER permission]" and organization_name becomes None.

Changes

Cohort / File(s) Change Summary
Cloud workspace API & types
airbyte/cloud/workspaces.py
Added CloudOrganization dataclass and a public CloudWorkspace.get_organization(..., raise_on_error=...) API with overloads. Replaced direct organization_id/organization_name accessors with the new retrieval method and added error handling via AirbyteError.
Workspace check behavior
airbyte/mcp/cloud_ops.py
Updated check_airbyte_cloud_workspace to call workspace.get_organization(raise_on_error=False) and build CloudWorkspaceResult from the optional organization object; introduced placeholder "[unavailable - requires ORGANIZATION_READER permission]" for missing organization_id and set organization_name to None when org info is unavailable.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review airbyte/cloud/workspaces.py for overload correctness, typing, and that AirbyteError covers all expected failure modes — should other exceptions be considered or wrapped? wdyt?
  • Verify caching semantics of _organization_info and that get_organization(..., raise_on_error=False) consistently returns None on all permission/fetch failures.
  • Validate callers of check_airbyte_cloud_workspace and any consumers of CloudWorkspaceResult correctly handle the placeholder organization_id string and None organization_name — would a structured sentinel or enum be preferable? wdyt?

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: handling permission errors gracefully in the check_airbyte_cloud_workspace tool when organization permissions are missing.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch devin/1765397982-fix-check-workspace-permissions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b48476 and aaec3ad.

📒 Files selected for processing (1)
  • airbyte/mcp/cloud_ops.py (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: yohannj
Repo: airbytehq/PyAirbyte PR: 716
File: airbyte/logs.py:384-402
Timestamp: 2025-07-11T19:53:44.427Z
Learning: In the PyAirbyte project, when reviewing PRs, maintain clear separation of concerns. Don't suggest changes that are outside the scope of the PR's main objective, even if they would improve consistency or fix other issues. This helps with reviewing changes and potential reverts.
📚 Learning: 2024-10-06T23:44:31.534Z
Learnt from: aaronsteers
Repo: airbytehq/PyAirbyte PR: 411
File: airbyte/cli.py:111-160
Timestamp: 2024-10-06T23:44:31.534Z
Learning: In PyAirbyte, error messages in functions like `_resolve_source_job` in `airbyte/cli.py` are designed to decouple the message text from dynamic values, following a structlog-inspired design. Dynamic values are provided via parameters like `input_value`. This approach helps avoid including PII in the message strings, which may be used in telemetry.

Applied to files:

  • airbyte/mcp/cloud_ops.py
🧬 Code graph analysis (1)
airbyte/mcp/cloud_ops.py (2)
airbyte/exceptions.py (3)
  • AirbyteError (432-447)
  • AirbyteMissingResourceError (505-509)
  • PyAirbyteInputError (201-210)
airbyte/cloud/workspaces.py (3)
  • organization_id (101-106)
  • organization_name (109-114)
  • workspace_url (83-85)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Pytest (All, Python 3.11, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.10, Windows)
  • GitHub Check: Pytest (All, Python 3.10, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.11, Windows)
  • GitHub Check: Pytest (Fast)
  • GitHub Check: Pytest (No Creds)
🔇 Additional comments (1)
airbyte/mcp/cloud_ops.py (1)

22-22: No action needed. AirbyteError is the correct exception type for 403 permission errors. The _make_config_api_request() function in api_util.py explicitly catches HTTP errors and raises AirbyteError for any non-2xx response, including 403 Forbidden. The try-except block in cloud_ops.py correctly handles this exception when accessing organization information without ORGANIZATION_READER permissions.

@aaronsteers Aaron ("AJ") Steers (aaronsteers) changed the title fix(mcp): Handle permission errors gracefully in check_airbyte_cloud_workspace (do not merge) fix(mcp): Handle permission errors gracefully in check_airbyte_cloud_workspace Dec 10, 2025
@github-actions
Copy link

github-actions bot commented Dec 10, 2025

PyTest Results (Fast Tests Only, No Creds)

348 tests  ±0   348 ✅ ±0   5m 42s ⏱️ -26s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 43829bb. ± Comparison against base commit 1b48476.

♻️ This comment has been updated with latest results.

…hod with overloads

- Create CloudOrganization dataclass as a minimal value object
- Add get_organization(raise_on_error: bool = True) method to CloudWorkspace
- Add overloads for proper type inference based on raise_on_error parameter
- Remove organization_id and organization_name properties (breaking change)
- Update check_airbyte_cloud_workspace MCP tool to use new method

This is a breaking change: callers should use workspace.get_organization()
instead of workspace.organization_id/organization_name.

Co-Authored-By: AJ Steers <aj@airbyte.io>
@devin-ai-integration devin-ai-integration bot changed the title fix(mcp): Handle permission errors gracefully in check_airbyte_cloud_workspace refactor(cloud): Add CloudOrganization class and get_organization method Dec 10, 2025
…nization_name

- Validate that both organization_id and organization_name are non-null and non-empty
- If validation fails, raise AirbyteError (or return None if raise_on_error=False)
- Update CloudWorkspaceResult docstrings to note ORGANIZATION_READER permission requirement

Co-Authored-By: AJ Steers <aj@airbyte.io>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
airbyte/cloud/workspaces.py (1)

116-131: The overload pattern looks correct, but note the CodeQL warnings.

The overload declarations are correctly structured to provide proper type hints:

  • Default/raise_on_error=TrueCloudOrganization (non-nullable)
  • raise_on_error=FalseCloudOrganization | None

Past review flagged that ... triggers "Statement has no effect" warnings from CodeQL. While ... (Ellipsis) is the idiomatic Python way to write stub method bodies, you could use pass instead to silence these warnings if they're noisy in your CI, wdyt?

     @overload
-    def get_organization(self) -> CloudOrganization: ...
+    def get_organization(self) -> CloudOrganization:
+        pass

     @overload
     def get_organization(
         self,
         *,
         raise_on_error: Literal[True],
-    ) -> CloudOrganization: ...
+    ) -> CloudOrganization:
+        pass

     @overload
     def get_organization(
         self,
         *,
         raise_on_error: Literal[False],
-    ) -> CloudOrganization | None: ...
+    ) -> CloudOrganization | None:
+        pass
🧹 Nitpick comments (1)
airbyte/mcp/cloud_ops.py (1)

497-502: Consider making organization_id nullable instead of using a placeholder string?

I notice from the past review discussion that using a placeholder string "[unavailable - requires ORGANIZATION_READER permission]" for organization_id was flagged as potentially problematic for downstream code that might:

  • Validate UUID format
  • Use the ID in API calls
  • Store it in databases with constraints

The current CloudWorkspaceResult.organization_id is typed as str (line 161). Would it make sense to change this to str | None and return None when unavailable, similar to how organization_name is handled? This would be more type-safe and prevent potential runtime errors downstream, wdyt?

That said, I see this was discussed and the current approach was approved, so feel free to keep as-is if there's context I'm missing!

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aaec3ad and 43829bb.

📒 Files selected for processing (2)
  • airbyte/cloud/workspaces.py (5 hunks)
  • airbyte/mcp/cloud_ops.py (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: yohannj
Repo: airbytehq/PyAirbyte PR: 716
File: airbyte/logs.py:384-402
Timestamp: 2025-07-11T19:53:44.427Z
Learning: In the PyAirbyte project, when reviewing PRs, maintain clear separation of concerns. Don't suggest changes that are outside the scope of the PR's main objective, even if they would improve consistency or fix other issues. This helps with reviewing changes and potential reverts.
📚 Learning: 2025-07-11T19:53:44.427Z
Learnt from: yohannj
Repo: airbytehq/PyAirbyte PR: 716
File: airbyte/logs.py:384-402
Timestamp: 2025-07-11T19:53:44.427Z
Learning: In the PyAirbyte project, when reviewing PRs, maintain clear separation of concerns. Don't suggest changes that are outside the scope of the PR's main objective, even if they would improve consistency or fix other issues. This helps with reviewing changes and potential reverts.

Applied to files:

  • airbyte/mcp/cloud_ops.py
🧬 Code graph analysis (2)
airbyte/mcp/cloud_ops.py (1)
airbyte/cloud/workspaces.py (5)
  • get_organization (117-117)
  • get_organization (120-124)
  • get_organization (127-131)
  • get_organization (133-180)
  • workspace_url (98-100)
airbyte/cloud/workspaces.py (1)
airbyte/exceptions.py (1)
  • AirbyteError (432-447)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Pytest (All, Python 3.11, Windows)
  • GitHub Check: Pytest (All, Python 3.11, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.10, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.10, Windows)
  • GitHub Check: Pytest (No Creds)
  • GitHub Check: Pytest (Fast)
🔇 Additional comments (4)
airbyte/mcp/cloud_ops.py (1)

161-164: LGTM!

The updated docstrings clearly communicate the permission requirements for these fields. This helps consumers of the API understand why these values might be unavailable. Nice touch! 👍

airbyte/cloud/workspaces.py (3)

41-41: LGTM!

The new imports for overload and AirbyteError are correctly added to support the new get_organization method.

Also applies to: 55-55


65-77: LGTM!

The CloudOrganization dataclass is clean and minimal - exactly what was requested. Nice use of a value object to encapsulate the organization info!


133-180: LGTM! Clean implementation with proper error handling.

The implementation looks solid:

  • Correctly catches AirbyteError for API/permission failures (confirmed in past discussion that this is the right exception for 403 errors)
  • Validates that both organization_id and organization_name are non-null and non-empty, as requested
  • The conditional re-raise pattern (if raise_on_error: raise) is clean
  • Good use of context in the error message for debugging

@aaronsteers Aaron ("AJ") Steers (aaronsteers) marked this pull request as ready for review December 10, 2025 21:04
@aaronsteers Aaron ("AJ") Steers (aaronsteers) changed the title refactor(cloud): Add CloudOrganization class and get_organization method fix(mcp): The check workspace tool should not fail when missing organization permissions. Dec 10, 2025
@aaronsteers Aaron ("AJ") Steers (aaronsteers) changed the title fix(mcp): The check workspace tool should not fail when missing organization permissions. fix(mcp): The check workspace tool should not fail when missing organization permissions Dec 10, 2025
@github-actions
Copy link

PyTest Results (Full)

416 tests  ±0   400 ✅ ±0   27m 16s ⏱️ + 2m 1s
  1 suites ±0    16 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 43829bb. ± Comparison against base commit 1b48476.

@aaronsteers Aaron ("AJ") Steers (aaronsteers) merged commit a7c9ccf into main Dec 10, 2025
27 of 28 checks passed
@aaronsteers Aaron ("AJ") Steers (aaronsteers) deleted the devin/1765397982-fix-check-workspace-permissions branch December 10, 2025 21:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant